import numpy as np
from scipy.stats import norm

from view.endSrc.tGaussianClustersEstimatorBy1D import tGaussianClustersEstimatorBy1D
from view.endSrc.PolarCoordConvert import PolarCoordConvert


class wGaussianClustersEstimatorBy1D:

    def __init__(self, dbconn):
        self.t = tGaussianClustersEstimatorBy1D(dbconn)

    def getDetailByObs(self, obIds, bestEstIds):
        aiccList = []
        bestFitAicc = []
        bestMix_compNum = []
        bestMix_aicc = []
        maxComp_Ob = []
        minComp_Ob = []
        for j, i in enumerate(obIds):
            rows = self.t.getEstimatorDetailByOb(i)
            aiccListOb = []

            maxComp_Ob.append(rows[-1][2])
            minComp_Ob.append(rows[0][2])

            for row in rows:
                estNegLogLikelihood = row[5]
                estRegularItem = row[6]

                if estNegLogLikelihood is None or estRegularItem is None:
                    aiccListOb.append([])
                    continue
                aiccListOb.append([row[2], estNegLogLikelihood + estRegularItem])
                if row[0] == bestEstIds[j]:
                    bestFitAicc.append([[row[2], estNegLogLikelihood + estRegularItem]])
                    bestMix_compNum.append(row[2])
                    bestMix_aicc.append(estNegLogLikelihood + estRegularItem)

            aiccList.append(aiccListOb)

        maxComp_AllOb = max(maxComp_Ob)

        return aiccList, bestFitAicc, bestMix_compNum, bestMix_aicc, maxComp_Ob, minComp_Ob, maxComp_AllOb

    def getBestEstimatorDetail(self, bestEstIds, distances):
        paiList = []
        meanList = []
        varList = []
        bestEst_maxComp = 0

        for i in bestEstIds:
            self.t.readRow(i)
            paiList.append(self.t.estMixedParams['paiList'])
            meanList.append(self.t.estMixedParams['meanList'])
            varList.append(self.t.estMixedParams['varList'])

            if len(self.t.estMixedParams['paiList']) > bestEst_maxComp:
                bestEst_maxComp = len(self.t.estMixedParams['paiList'])

        den_mix_best = []
        for i in range(len(bestEstIds)):  # observer
            minima = min(distances[i])
            maxima = max(distances[i])
            st = np.linspace(minima, maxima, 1000)
            den_mix_best_est = []
            for j in range(len(paiList[i])):  # component
                comp = norm.pdf(st, meanList[i][j], np.sqrt(varList[i][j]))
                comp = comp * paiList[i][j]
                den_mix_best_est.append([[st[j], comp[j]] for j in range(len(st))])
            den_mix_best.append(den_mix_best_est)

        return bestEst_maxComp, den_mix_best

    def getEstIdBasedObAndCompnum(self, obId, numOfComp):
        estimatorDetail = self.t.getEstimatorDetailByOb(obId)
        for row in estimatorDetail:
            if row[2] == numOfComp:
                tEstId = row[0]
                break
        return tEstId

    def getProbMat(self, tEstId):
        self.t.readRow(tEstId)
        return self.t.m_estProbMatrix

    def getDen(self, tEstId, numOfComp, st):
        self.t.readRow(tEstId)

        paiList = self.t.estMixedParams['paiList']
        meanList = self.t.estMixedParams['meanList']
        varList = self.t.estMixedParams['varList']

        den_mix = []
        den_mix_sum = np.zeros(st.shape)
        for i in range(numOfComp):
            comp = norm.pdf(st, meanList[i], np.sqrt(varList[i]))
            comp = comp * paiList[i]
            den_mix_sum += comp
            den_mix.append([[st[j], comp[j]] for j in range(len(st))])

        den_mix_sum = [[st[j], den_mix_sum[j]] for j in range(len(st))]

        return den_mix, den_mix_sum

    def getPolar(self, tEstId, numOfComp, dataset, trueCenters, observer):
        self.t.readRow(tEstId)

        estProbMat = self.t.m_estProbMatrix
        if len(estProbMat.shape) == 1:
            estProbMat = estProbMat[:, np.newaxis]
        label = np.argmax(estProbMat, axis=1)

        polarDataset_mix = []
        for i in range(numOfComp):
            idx = np.where(label == i)[0]
            polarDataset_mix.append(PolarCoordConvert.convert(observer, (np.array(dataset)[idx, :]).tolist()))

        polarCenters_mix = PolarCoordConvert.convert(observer, trueCenters)

        return polarDataset_mix, polarCenters_mix

    def getEstAbsAndResult(self, tEstId):
        self.t.readRow(tEstId)

        negLogLikelihood = self.t.estNegLogLikelihood
        regItem = self.t.estRegularItem

        estAbs = {'negLogLikelihood': negLogLikelihood,
                  'regItem': regItem}

        estLbd = self.t.estMixedParams['paiList']
        estMeans= self.t.estMixedParams['meanList']
        estVars = self.t.estMixedParams['varList']

        estPara = {'estLbd': estLbd,
                   'estScales': estVars,
                   'estShapes': estMeans}

        return estAbs, estPara
